﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Reflection;

using ApplicationBoilerplate.DataProvider;
using ApplicationBoilerplate.DependencyInjection;

namespace MVCBootstrap.EntityFramework {

	public class Context : ContextBase {
		private readonly DbContext context;
		private readonly Stack<DbTransaction> stack = new Stack<DbTransaction>();

		public Context(DbContext context, IDependencyContainer container)
			: base(container) {
			this.context = context;
		}

		public override IRepository<TEntity> GetRepository<TEntity>() {
			return new Repository<TEntity>(this.context);
		}

		//public override IUnitOfWork CreateUnitOfWork() {
		//    DbTransaction trans = this.context.Database.Connection.BeginTransaction();
		//    this.stack.Push(trans);
		//    return new UnitOfWork(trans);
		//}

		public override void SaveChanges() {
			// Apart from the actual this.context.SaveChanges() line, this method is all about
			// working around a known issue with lazy loaded properties with the Required attribute.
			try {
				// Let's get any validation errors up-front!
				IEnumerable<DbEntityValidationResult> errors = this.context.GetValidationErrors();
				// Iterate them!
				foreach (DbEntityValidationResult result in errors) {
					// Get the base type of the entity that is causing the error. The entity is actually a proxy object
					// created by EF, so we need the Type of the actual class, as we need the attributes from the properties
					// on the class we have created (Forum, Post, Topic, etc.)!
					Type baseType = result.Entry.Entity.GetType().BaseType;
					// Run through the properties.
					foreach (PropertyInfo property in result.Entry.Entity.GetType().GetProperties()) {
						// Is this property marked with the Required attribute (on the base type)??
						if (baseType.GetProperty(property.Name).GetCustomAttributes(typeof(RequiredAttribute), true).Any()) {
							// Yes, let's get it's value (from the proxy object), that will load any lazy loaded property and solve:
							// Required (lazy loaded) property does not have a value!!
							property.GetValue(result.Entry.Entity, null);
						}
					}
				}
			}
			catch {
				throw;
			}

			// Time to save the changes.
			this.context.SaveChanges();
		}

		public override void Dispose(Boolean disposing) {
			if (disposing) {
				this.context.Dispose();
			}
		}
	}
}